﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace Cheetah_Fast_Banking_App
{
    public class BillPayManager
    {
        //public void RunBillPay()
        //{
        //    // get last date BillPay was run
        //    DateTime lastRun = DataSourceSingleton.GetInstance().LastDateBillPayRun;
        //    DateTime now = DateTime.Now;
        //    DateTime today = new DateTime(now.Year, now.Month, now.Day); // round down to midnight

        //    while (lastRun < today)
        //    {
        //        oldLastRun = lastRun;
        //        lastRun = lastRun + new TimeSpan(1, 1, 0, 0);
        //        lastRun = new DateTime(lastRun.Year, lastRun.Month, lastRun.Day); // round down to midnight
        //        RunBillPay(lastRun);
        //    }
        //    DataSourceSingleton.GetInstance().LastDateBillPayRun = new DateTime(lastRun.Year, lastRun.Month, lastRun.Day); // round down to midnight
        //}

        public void RunBillPay()
        {
            DateTime now = DateTime.Now;
            DateTime today = new DateTime(now.Year, now.Month, now.Day); // midnight
            RunBillPay(today);
        }

        public void RunBillPay(DateTime today)
        {
            foreach (Account account in DataSourceSingleton.GetInstance().Accounts)
            {
                RunBillPay(account, today);
            }
        }

        public void RunBillPay(Account account, DateTime today)
        {
            // get all active bill pays with start on or before today and end on or after today
            ObservableCollection<BillPay> billPays = account.BillPays;
            var query = billPays.Where(b => b.Active && b.FirstDate <= today && (!b.LastDate.HasValue || b.LastDate.Value >= today));

            // for each bill pay
            foreach (BillPay bill in query)
            {
                DateTime payDate = bill.Interval == Interval.Monthly ? GetMonthlyPayDate(today, bill.FirstDate) : today;
                if (ShouldPayBill(account, today, bill, payDate))
                    PayBill(account, bill, payDate);
            }
        }

        private bool ShouldPayBill(Account account, DateTime today, BillPay bill, DateTime payDate)
        {
            return TodayIsPayDay(today, bill.FirstDate, bill.Interval) && !HasBeenPaid(account, bill.ID.ToString(), payDate);
        }

        private bool TodayIsPayDay(DateTime today, DateTime startDate, Interval interval)
        {
            switch (interval)
            {
                case Interval.Daily: return true;
                case Interval.Weekly: return ((int)(today - startDate).TotalDays) % 7 == 0;
                case Interval.Monthly: return today == GetMonthlyPayDate(today, startDate);
                case Interval.Yearly: return today.DayOfYear == startDate.DayOfYear;
            }
            return false;
        }

        private DateTime GetMonthlyPayDate(DateTime today, DateTime startDate)
        {
            int day = startDate.Day;
            if (day <= 28)
                day = startDate.Day;
            if (today.Month == 2) // February
            {
                if (!IsLeapYear(today.Year) && day > 28) // 28 days
                    day = 28;
                else if (IsLeapYear(today.Year) && day > 29) // 29 days
                    day = 29;
            }
            else if (today.Month == 4 || today.Month == 6 || today.Month == 9 || today.Month == 11) // 30-day month
            {
                if (day >= 30)
                    day = 30;
            }

            return new DateTime(today.Year, today.Month, day);
        }

        private bool IsLeapYear(int year)
        {
            return ((year % 4) == 0) && ((year % 100) != 0);
        }

        private bool HasBeenPaid(Account account, string billId, DateTime payDate)
        {
            // get all transactions with billId==billId, date==today
            var query = account.Transactions.Where(t => t.BillId.ToString() == billId && t.Date == payDate);

            if (query.Count() == 0)
                return false;
            else// if (query.Count() == 1)
                return true;
            //else
            //    throw new DuplicateBillPayException("bill paid more than once");
        }

        private void PayBill(Account account, BillPay bill, DateTime today)
        {
            Transaction transaction = new Transaction(bill.Description, today, -bill.Amount);
            transaction.BillId = bill.ID;
            account.SaveNewTransaction(transaction);
        }
    }
}
